package com.sluggard.security.core.manage;

import com.sluggard.common.exception.LuaExecuteException;
import com.sluggard.common.exception.LuaFileNotFoundException;
import com.sluggard.security.properties.SecurityConfigProperties;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author lizheng
 * @since 2024.4.22
 */
@Component
public class CaptchaCacheManage {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ResourceLoader resourceLoader;

    @Resource
    private SecurityConfigProperties securityConfigProperties;

    public static final String LOGIN_ERROR_TIMES = "login:error:times:";
    public static final String LOGIN_CAPTCHA = "login:captcha:";
    public static final String EXECUTE_SUCCESS = "OK";

    /**
     * 登录失败增加失败次数
     *
     * @param username 用户名
     */
    public void loginErrorTimesIncrement(String username) {
        if (!securityConfigProperties.getCaptchaEnable()) {
            return;
        }
        String result = executeLuaScript("increment", List.of(LOGIN_ERROR_TIMES + username));
        // 调用成功
        if (!StringUtils.equalsAnyIgnoreCase(EXECUTE_SUCCESS, result)) {
            throw new LuaExecuteException("LUA脚本调用失败");
        }
    }

    /**
     * 登陆成功清除缓存
     *
     * @param username 用户名
     */
    public void clearLoginErrorTimes(String username) {
        if (!securityConfigProperties.getCaptchaEnable()) {
            return;
        }
        stringRedisTemplate.delete(LOGIN_ERROR_TIMES + username);
    }

    /**
     * 获取登录失败次数
     *
     * @param username 用户名
     * @return 失败次数
     */
    public Integer getLoginErrorTimes(String username) {
        if (!securityConfigProperties.getCaptchaEnable()) {
            return 0;
        }
        String loginErrorTimes = stringRedisTemplate.boundValueOps(LOGIN_ERROR_TIMES + username).get();
        if (StringUtils.isBlank(loginErrorTimes)) {
            return 0;
        }
        return Integer.parseInt(loginErrorTimes);
    }

    /**
     * 保存验证码并设置有效期
     *
     * @param key 缓存键
     * @param captcha  验证码
     */
    public void saveCaptcha(String key, String captcha, Long expireTime, TimeUnit unit) {
        stringRedisTemplate.boundValueOps(LOGIN_CAPTCHA + key).set(captcha, expireTime, unit);
    }

    /**
     * 清除验证码
     * @param key 缓存键
     */
    public void clearCaptcha(String key){
        stringRedisTemplate.delete(LOGIN_CAPTCHA + key);
    }

    /**
     * 获取验证码
     *
     * @param key 缓存键
     */
    public String getCaptcha(String key) {
        return stringRedisTemplate.boundValueOps(LOGIN_CAPTCHA + key).get();
    }

    /**
     * 执行lua脚本
     *
     * @param scriptName 脚本名称
     * @param keys       缓存键集合
     * @param args       参数
     * @return 脚本执行结果
     */
    public String executeLuaScript(String scriptName, List<String> keys, Object... args) {
        String luaScript = readLuaScript(scriptName);
        // 定义Lua脚本
        DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(String.class);
        // 执行Lua脚本
        return stringRedisTemplate.execute(redisScript, keys, args);
    }

    /**
     * @param scriptName 脚本文件名
     * @return 脚本内容
     */
    @SneakyThrows
    public String readLuaScript(String scriptName) {
        try {
            org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:lua/" + scriptName + ".lua");
            StringBuilder scriptBuilder = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    scriptBuilder.append(line).append("\n");
                }
            }
            return scriptBuilder.toString();
        } catch (FileNotFoundException exception) {
            throw new LuaFileNotFoundException(scriptName);
        }
    }
}
